package services

import (
	"encoding/json"
	"fmt"
	"go.uber.org/zap"
	"io/ioutil"
	"net/http"
	"net/url"
	"purchase-mall/dao/redis"
	"purchase-mall/models"
	"purchase-mall/services/service"
	"purchase-mall/utils"
	"strconv"
	"sync"
	"time"
)

type IpToken struct {
	Ip    string
	Token string
}

//GetIpListService 获取ip列表
func GetIpListService(ipSearch *service.IpListSearch) (data *models.IpList, err error) {
	var endTime int64
	var days int

	if ipSearch.Filters.QuickDate != "" {
		switch ipSearch.Filters.QuickDate {
		case "today":
			days = 1
			endTime = utils.TodayTime()
		case "yesterday":
			days = 1
			endTime = utils.YesterdayTime()
		case "week":
			days = 7
			endTime = utils.TodayTime()
		case "month":
			days = 30
			endTime = utils.TodayTime()
		case "year":
			days = 365
			endTime = utils.TodayTime()
		}
	} else if ipSearch.Filters.Date != nil && len(ipSearch.Filters.Date) == 2 {
		days = int(utils.GetTimeArr(ipSearch.Filters.Date[0], ipSearch.Filters.Date[1]))
		t, _ := time.ParseInLocation("2006-01-02 15:04:05", ipSearch.Filters.Date[1], time.Local)
		endTime = t.UnixNano()
	} else {
		days = 1
		endTime = utils.TodayTime()
	}

	params := url.Values{}
	parseURL, err := url.Parse(utils.RequestGetUrlNoParams)
	if err != nil {
		return
	}
	if ipSearch.Filters.Ip != "" {
		params.Set("query", fmt.Sprintf("sum by(ip,token) (count_over_time({filename=\"/apisix/log/error.log\",from=\"access log\",env=\"prod\",exporter=\"promtail\"} | json | ip!=\"\" | json | ip=\"%s\" | json ip = \"ip\" | json token = \"token\" | __error__!=\"JSONParserErr\" [%dd]))", ipSearch.Filters.Ip, days))
	} else {
		params.Set("query", fmt.Sprintf("sum by(ip,token) (count_over_time({filename=\"/apisix/log/error.log\",from=\"access log\",env=\"prod\",exporter=\"promtail\"} | json | ip!=\"\" | json ip = \"ip\" | json token = \"token\" | __error__!=\"JSONParserErr\" [%dd]))", days))
	}

	params.Set("time", strconv.FormatInt(endTime, 10))
	parseURL.RawQuery = params.Encode()
	urlPathWithParams := parseURL.String()

	client := http.Client{
		Timeout: 10 * time.Minute,
	}

	resp, err := client.Get(urlPathWithParams)
	if err != nil {
		zap.L().Error("Loki Http Api GetIpListService failed",
			zap.Int64("time", endTime),
			zap.Error(err))
		return
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}
	var res RespJson
	err = json.Unmarshal(body, &res)
	if err != nil {
		return
	}

	if res.Status != "success" {
		return
	}

	data = &models.IpList{
		PageNum:  ipSearch.PageNum,
		PageSize: ipSearch.PageSize,
	}

	var ipsMap = make(map[string]*models.Ip)

	for _, s := range res.Data.Result {
		var requestNum int
		if s.Value != nil && len(s.Value) >= 2 {
			requestNum, _ = strconv.Atoi(s.Value[1].(string))
		}

		var userInfo = models.UserInfo{
			Token: s.Metric.Token,
		}

		//判断ip是否重复
		if v, ok := ipsMap[s.Metric.Ip]; ok {
			//重复
			v.UseTotal += int64(requestNum)
			v.UserInfo = append(v.UserInfo, &userInfo)
		} else {
			var ip = &models.Ip{
				Ip:        s.Metric.Ip,
				Timestamp: endTime,
				UseTotal:  0,
			}
			ip.UserInfo = make([]*models.UserInfo, 0)

			ip.UseTotal = int64(requestNum)
			ip.UserInfo = append(ip.UserInfo, &userInfo)

			ipsMap[s.Metric.Ip] = ip
		}
	}
	ips := make([]*models.Ip, 0, len(ipsMap))

	data.Total = int64(len(ipsMap))

	for _, v := range ipsMap {
		ips = append(ips, v)
	}

	//排序
	BubbleSortIps(ips)
	start := int((data.PageNum - 1) * data.PageSize)
	end := int(data.PageNum * data.PageSize)
	if start > len(ips) {
		data.List = make([]*models.Ip, 0)
		return
	}
	if end > len(ips) {
		data.List = ips[start:len(ips)]
		return
	}
	data.List = ips[start:end]

	//userinfo
	var wg sync.WaitGroup
	wg.Add(len(data.List))
	for _, v := range data.List {
		go func(v *models.Ip) {
			defer wg.Done()

			for _, user := range v.UserInfo {
				if user.Token != "" {
					tokenInfos, err := GetTokenInfo(user.Token)
					if err == nil {
						if tokenInfos.ResponseJson != nil && len(tokenInfos.ResponseJson) > 0 {
							user.Level = tokenInfos.ResponseJson[0].TrafficLevel
							user.NickName = tokenInfos.ResponseJson[0].NickName
							user.Quota = tokenInfos.ResponseJson[0].Traffic.Quota
							user.Account = tokenInfos.ResponseJson[0].Account
						}
					}

					user.IsBlock = redis.IsBlock(user.Token)
				}
			}
		}(v)
	}
	wg.Wait()

	return
}
